/**
 *  This file is part of Libgdx by Mario Zechner (badlogicgames@gmail.com)
 *
 *  Libgdx is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Libgdx is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package ndquangr.intern.ringtonemaker;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

/**
 * A native MP3 {@link Decoder} based on libmad. Decodes
 * stereo input to mono.
 *
 * @author badlogicgames@gmail.com
 *
 */
public class NativeMP3Decoder implements Decoder
{      
		static {
			try {
				System.loadLibrary("mad");
			} catch (UnsatisfiedLinkError e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
		}
        /** the handle to the native mp3 decoder **/
        private int handle;
       
        /** the float buffer used to read in the samples **/
        private FloatBuffer buffer;
       
        /** the short buffer **/
        private ShortBuffer shortBuffer;
        
        /*
         * Header of mp3 file
         * header[0] = samplerate
		 * header[1] = bitrate
		 * header[2] = channels
		 * header[3] = size
         */
        private int [] header;
       
        /**
         * Constructor, sets the file to decode. The file is given in absolute terms! FIXME
         *
         * @param file The file.
         */
        public NativeMP3Decoder( String file )
        {
                handle = openFile( file );
                if( handle == -1 ) {
                    throw new IllegalArgumentException( "Couldn't open file '" + file + "'" );
                }                
        }
               
        private native int openFile( String file );
       
        /**
         * Reads in numSamples float PCM samples to the provided direct FloatBuffer using the
         * handle retrievable via {@link NativeMP3Decoder.getHandle()}. This is for
         * people who know what they do. Returns the number of samples actually read.
         *
         * @param handle The handle
         * @param buffer The direct FloatBuffer
         * @param numSamples The number of samples to read
         * @return The number of samples read.
         */
        public native int readSamples( int handle, FloatBuffer buffer, int numSamples );

        /**
         * Reads in numSamples 16-bit signed PCM samples to the provided direct FloatBuffer using the
         * handle retrievable via {@link NativeMP3Decoder.getHandle()}. This is for
         * people who know what they do. Returns the number of samples actually read.
         *
         * @param handle The handle
         * @param buffer The direct FloatBuffer
         * @param numSamples The number of samples to read
         * @return The number of samples read.
         */
        public native int readSamples( int handle, ShortBuffer buffer, int numSamples );
       
        private native void closeFile( int handle );
        
        private native int [] readHeader( int handle);
        
        public native void readSamples( int handle, ShortBuffer buffer, long length, long step );
        
        public native int writeDataToFile(int startTime, int endTime, int handle, String filePath);
       
        /**
         * @return The handle retrieved from the native side.
         */
        public int getHandle( )
        {
                return handle;
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public int readSamples(float[] samples)
        {      
                if( buffer == null || buffer.capacity() != samples.length )
                {
                        ByteBuffer byteBuffer = ByteBuffer.allocateDirect( samples.length * Float.SIZE / 8 );
                        byteBuffer.order(ByteOrder.nativeOrder());
                        buffer = byteBuffer.asFloatBuffer();
                }
               
                int readSamples = readSamples( handle, buffer, samples.length );
                if( readSamples == 0 )
                {
                        closeFile( handle );
                        return 0;
                }
               
                buffer.position(0);
                buffer.get( samples );
               
                return samples.length;
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public int readSamples(short[] samples)
        {      
        	if( shortBuffer == null || shortBuffer.capacity() != samples.length )
            {
                    ByteBuffer byteBuffer = ByteBuffer.allocateDirect( samples.length * Short.SIZE / 8 );
                    byteBuffer.order(ByteOrder.nativeOrder());
                    shortBuffer = byteBuffer.asShortBuffer();
            }
            
            int readSamples = readSamples( handle, shortBuffer, samples.length );
            if( readSamples == 0 )
            {
                    closeFile( handle );
                    return 0;
            }
            
            shortBuffer.position(0);
            shortBuffer.get( samples );
            
            return samples.length;

        }

        /**
         * {@inheritDoc}
         */
        public void dispose( )
        {
                closeFile(handle);
        }
        
        @Override
        public int readCheapSamples(short [] samples, long step){
        	long buffer_length = samples.length;
        	if( shortBuffer == null || shortBuffer.capacity() != samples.length )
            {
                    ByteBuffer byteBuffer = ByteBuffer.allocateDirect( samples.length * Short.SIZE / 8 );
                    byteBuffer.order(ByteOrder.nativeOrder());
                    shortBuffer = byteBuffer.asShortBuffer();
            }
        	
        	readSamples(handle, shortBuffer, buffer_length, step);
        	shortBuffer.position(0);
            shortBuffer.get(samples );
            
            return samples.length;
        }
        
        @Override
        public int readCheapSamples(short [] samples){
        	header = readHeader(handle);
        	int step = header[0]/100 - 1;
        	long buffer_length = samples.length;
        	if( shortBuffer == null || shortBuffer.capacity() != samples.length )
            {
                    ByteBuffer byteBuffer = ByteBuffer.allocateDirect( samples.length * Short.SIZE / 8 );
                    byteBuffer.order(ByteOrder.nativeOrder());
                    shortBuffer = byteBuffer.asShortBuffer();
            }
        	
        	readSamples(handle, shortBuffer, buffer_length, step);
        	shortBuffer.position(0);
            shortBuffer.get(samples );
            
            return samples.length;
        }
        
        @Override
        public int[] readHeader(){
        	if (handle != -1) {      		
        		int[] header = readHeader(handle);
        		return header;
        	} else {
        		return null;
        	}
        }
}

